package com.rslai.commons.util;

import com.rslai.commons.util.exception.RunException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

/**
 * 文件分割工具类
 * Created by rslai on 2018/12/6.
 */
public class FileSplit {
    private static final Logger LOGGER = LoggerFactory.getLogger(FileSplit.class);

    /**
     * <p>根据指定行数切割文件</p>
     *
     * @param fileName  待切割的文件
     * @param groupRows 将多少行内容存入一个新文件
     * @return {@code true} 切割成功
     * @throws IOException IOException
     */
    public boolean byRow(String fileName, long groupRows) throws IOException {
        return byRow(fileName, null, groupRows);
    }

    /**
     * <p>根据指定行数切割文件</p>
     *
     * @param fileName  待切割的文件
     * @param destPath  切割后的文件存入的路径
     * @param groupRows 将多少行内容存入一个新文件
     * @return {@code true} 切割成功
     * @throws IOException IOException
     */
    public boolean byRow(String fileName, String destPath, long groupRows) throws IOException {
        if (groupRows == 0) {
            throw new IllegalArgumentException("参数错误 fileRows 不能为 0！");
        }

        FileUtil fileUtil = new FileUtil();
        if (!fileUtil.isFile(fileName)) {
            throw new RunException("未找到待拆分的文件！" + fileName);
        }
        File srcFile = new File(fileName); // 待拆分文件

        String destFileNmae; // 目的文件名
        String srcFileName = fileUtil.getName(fileName); // 源文件名称
        if (StringUtil.isNotBlank(destPath)) {
            if (!fileUtil.isDirectory(destPath)) {
                throw new RunException("未找到输出路径！" + destPath);
            }
            destFileNmae = destPath + "/" + srcFileName;
        } else {
            destFileNmae = fileName;
        }

        long fileNo = 1; // 拆分文件编号
        long buffGroupRows = 10000; // 当拆分文件行数过大时，按照1万行分块存储

        StringBuffer sb = new StringBuffer(); // 读入文件缓冲
        BufferedReader bufReader = null;
        long tempGroupRows = 0; // 临时文件行号
        try {
            LOGGER.info("开始写入文件 " + fileUtil.createNewNname(destFileNmae, "_" + fileNo));
            fileUtil.delete(fileUtil.createNewNname(destFileNmae, "_" + fileNo)); // 删除待写入文件
            bufReader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));

            // 循环读取文件
            String tempString = null;
            while ((tempString = bufReader.readLine()) != null) {
                sb.append(tempString + "\n"); // 追加一行内容到分组缓存数据
                tempGroupRows++;
                if (tempGroupRows == groupRows) {
                    fileUtil.writeAppend(fileUtil.createNewNname(destFileNmae, "_" + fileNo), sb.toString()); // 分组写入文件

                    fileNo++; // 文件名+1
                    LOGGER.info("开始写入文件 " + fileUtil.createNewNname(destFileNmae, "_" + fileNo));
                    fileUtil.delete(fileUtil.createNewNname(destFileNmae, "_" + fileNo)); // 删除待写入文件

                    // 清空分组缓存数据
                    tempGroupRows = 0;
                    sb = new StringBuffer();
                }
                if ((tempGroupRows != 0) && ((tempGroupRows % buffGroupRows) == 0)) {
                    fileUtil.writeAppend(fileUtil.createNewNname(destFileNmae, "_" + fileNo), sb.toString()); // 分组写入文件
                    sb = new StringBuffer();
                }
            }

            if (sb.length() != 0) {
                fileUtil.writeAppend(fileUtil.createNewNname(destFileNmae, "_" + fileNo), sb.toString()); // 分组写入文件
            }
        } finally {
            if (bufReader != null) {
                bufReader.close();
                LOGGER.info("文件拆分完成！");
            }
        }

        return true;
    }

    /**
     * <p>根据字节数切割文件</p>
     *
     * @param fileName 待切割的文件
     * @param destPath 切割后的文件存入的路径
     * @param block    将文件按照多少block切割，1block = 10M
     */
    private static void bySize(String fileName, String destPath, int block) {
        if (block == 0) {
            throw new IllegalArgumentException("参数错误 size 不能为 0！");
        }

        FileUtil fileUtil = new FileUtil();
        if (!fileUtil.isFile(fileName)) {
            throw new RunException("未找到待拆分的文件！" + fileName);
        }
        File srcFile = new File(fileName); // 待拆分文件

        String destFileNmae; // 目的文件名
        String srcFileName = fileUtil.getName(fileName); // 源文件名称
        if (StringUtil.isNotBlank(destPath)) {
            if (!fileUtil.isDirectory(destPath)) {
                throw new RunException("未找到输出路径！" + destPath);
            }
            destFileNmae = destPath + "/" + srcFileName;
        } else {
            destFileNmae = fileName;
        }

        FileInputStream fis = null;
        File file = null;
        try {
            fis = new FileInputStream(fileName);

            byte[] buff = new byte[1024 * 1024 * 10]; // 按照10M一块拆分大文件

            int tmpBlock = 0; // 临时记录保存到多少块
            int len = 0;
            long fileNo = 1; // 拆分文件编号

            LOGGER.info("开始写入文件 " + fileUtil.createNewNname(destFileNmae, "_" + fileNo));
            fileUtil.delete(fileUtil.createNewNname(destFileNmae, "_" + fileNo)); // 删除待写入文件
            FileOutputStream fos = new FileOutputStream(fileUtil.createNewNname(destFileNmae, "_" + fileNo));

            while ((len = fis.read(buff)) != -1) { // 遍历将大文件读入byte数组中，当byte数组读满后写入对应的小文件中
                fos.write(buff, 0, len); // 将byte数组写入对应的小文件中

                if (++tmpBlock == block) {
                    fileNo++; // 文件名+1
                    tmpBlock = 0;
                    LOGGER.info("开始写入文件 " + fileUtil.createNewNname(destFileNmae, "_" + fileNo));
                    fileUtil.delete(fileUtil.createNewNname(destFileNmae, "_" + fileNo)); // 删除待写入文件
                    fos.close(); // 结束资源
                    fos = new FileOutputStream(fileUtil.createNewNname(destFileNmae, "_" + fileNo));
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fis != null) {
                    fis.close(); //结束资源
                    LOGGER.info("文件拆分完成！");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
